Blueprint Help Send comments on this topic.
Method Code

Glossary Item Box

Back to Entering Data Structures

Entering Method Code

There are 3 Methods in this application (and 1 Callback Function which we will deal with in a separate section) Generator, Calc and Pixellate.

Generator

Generator is the easiest so we will start here.

Generator is triggered to run by the GUI signalling the semaphore. It should only run once (however, an enhancement would be to 'reset' the application). Each of the NB methods is responsible for initializing 3 data stores Top, Mid and Bottom for its band. Since we have already defined an Initialise() function for the data types we simply need to construct the Records and initialize the data.

Double Click the Method (or select Edit Code->Process... in the Right Click menu)

MyCircuit_GeneratorMthdElemProc.cpp

...

Uns MyCircuit_GeneratorMthdElem::Process()
{
   // TODO: Add custom process code

   // construct and initialise output
   this->Trigger2_StripTopTst1Rec().Construct( 1 );
   this->Trigger2_StripTopTst1Rec().Initialise();

   this->Trigger2_MidBandTst1Rec().Construct( NUM_ROWS );
   this->Trigger2_MidbandTst1Rec().Initialise();

   this->Trigger2_StripBottomTst1Rec().Construct( 1 );
   this->Trigger2_StripBottomTst1Rec().Initialise();

   return TRUE;
}


Pixellate

The Pixellate method is the next simplest method. It simply calculates a pixel color value for each cell. To do this we require a color lookup table, as these are constants they can be held in workspace.

Entering Workspace Code

Note the numbering in the access function names, Trigger2 is always the curved face in an Sequential Collector Method and Trigger1 the flat face. However, in a Random Collector Method the distinction between Trigger1 and Trigger2 is less obvious. It is not usually a problem if the faces connect to different object names. If they connect to the same object (ie for read and write) then checking the Consumer Label for one of the connections will reveal its trigger name.

The number in the Tst part of the name is a counter for the number of connections to that object, which is why we connected the circuit in a particular order.

Using the Visual Studio Intellisense feature (invoked by using the this-> ) usually allows the correct names to be identified quickly. If there are any problems compiling this function it will probably be due to the connection numbering to the Pixels TST.

MyCircuit_PixellateMthdElemProc.cpp

The CalcPixels function could be added to a separate utilities library file

void CalcPixels( Char*& OutArray, Float*& InArray, Uns* ColourMap, 
                 const Uns GRID, const Uns COLOUR_PLANES, const Uns LOOKUP_LEVELS )
{
   for ( Uns c = 0; c < GRID; ++c )
   {
      // scale in value cast
      Float Value = *InArray * LOOKUP_LEVELS;

      // cast to unsigned int
      Uns Idx = Value > 0.0f ? (Uns) Value : 0;
      if ( Idx >= LOOKUP_LEVELS )
         Idx = LOOKUP_LEVELS - 1;

      // get color from look up table
      memcpy( OutArray, &ColourMap[Idx], COLOUR_PLANES );

      // increment pointers
      ++InArray;
      OutArray  +=
COLOUR_PLANES;
   }
}

Uns MyCircuit_PixellateMthdElem::Process()
{
   // TODO: Add custom process code

   // construct and take reference to output
   this->Trigger1_PixelsTst2Rec().Construct();
   Pixels& pixels = this->Trigger1_PixelsTst2Rec().Data();

   // pointer to first location in pixmap - calc functions will increment pointer
   Char*   outArray  = pixels.Values();

   // pointer to first location in in data - calc functions will increment pointer
   Float*   inArray = NULL;

   // pointer to in data
   Band*   band = NULL;

   // get colourMap from Workspace
   Uns* colourMap = this->Workspace().Data().Colours();

   for ( Uns b = 0; b < NB; ++b )
   {
      // bottom strip
      band = this->Trigger2_StripBottomTst2Rec( b ).Struct();
      inArray = band->Values();

      CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS );  

      // mid beam - repeat for each row
      band = this->Trigger2_MidBandTst2Rec( b ).Struct();
      inArray = band->Values();

      for ( Uns r = 0; r < NUM_ROWS; ++r )
      {
         CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS ); 

      }

       // top strip
      band = this->Trigger2_StripTopTst2Rec( b ).Struct();
      inArray = band->Values();

      CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS ); 
  }

   // update output frame number - use last band
   pixels.FrameCount() = band->FrameCount();
 
   return TRUE;
}


Calc

Calc calculates the wave parameters for each cell in each row, using data from its 8 neighbors (and itself) and writes the result to the next frame. Boundary cells need to handle the missing neighbor boundaries.

MyCircuit_CalcMthdElemProc.cpp

Dont worry about the details of F() or Wave() these are just providing a mathematical function to generate a wave dissipation plot. They is neither physically accurate or optimized.

The F and Wave functions could be added to a separate utilities library file

Float F( Float* Top, Float* Mid, Float* Btm )
{
   const Uns L = 0;
   const Uns M = 1;
   const Uns R = 2;
   const Float scale = 0.707f; // 1 / sqrt( 2 )

   Float sum = scale * Top[L] +        Top[M] + scale * Top[R] +
                       Mid[L] +                         Mid[R] +
               scale * Btm[L] +        Btm[M] + scale * Btm[R];

   sum *= 0.14644f;  // magic number
   sum -= Mid[M] * 0.01f;

   if ( sum > 1.0f )
      sum = 1.0f;


   return sum;
}

void Wave( Band* TopOut, Band* MidOut, Band* BtmOut,
           Band* BtmPrvIn, Band* TopIn, Band* MidIn, Band* BtmIn, Band* TopNxtIn,
           const Uns GRID, const Uns NROWS )
{
   Uns r_m1;
   Uns r_0;
   Uns r_p1;
   const Uns c_first = 0;
   const Uns c_last = GRID - 1;

   // btm strip
   r_p1 = 0;
   if ( PrvTopIn == NULL )
   {
      // boundary strip

      for ( Uns c = c_first; c <= c_last; ++c )
      {
         BtmOut[c] = MidIn[r_p1+c] * 0.5f;
      }
   }
   else
   {
      // boundary cells
      BtmOut[c_first] = BtmIn[c_first+1] * 0.5f;
      BtmOut[c_last] = BtmIn[c_last-1] * 0.5f;

      // rest of row
      for ( Uns c = 1; c < c_last; ++c )
      {
         const Uns c_1 = c-1;
         BtmOut[c] = F( &MidIn[r_p1+c_1], &BtmIn[c_1], &PrvTopIn[c_1] );
      }
   }

   // btm of mid band
   r_p1 = GRID;
   r_0 = 0;

   // boundary cells
   MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
   MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;
 
   // rest of row
   for ( Uns c = 1; c < c_last; ++c )
   {
      const Uns c_1 = c-1;
      MidOut[r_0+c] = F( &MidIn[r_p1+c_1], &MidIn[r_0+c_1], &BtmIn[c_1] );
   }
 
   // mid of mid band
   r_m1 = 0;
   r_0 = GRID;
   r_p1 = r_0 + GRID;

   for ( Uns r = 1; r < NROWS-1; ++r )
   {
      // boundary cells
      MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
      MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;

      // rest of row
      for ( Uns c = 1; c < c_last; ++c )
      {
         const Uns c_1 = c-1;
         MidOut[r_0+c] = F( &MidIn[r_p1+c_1], &MidIn[r_0+c_1], &MidIn[r_m1+c_1] );
      }

      r_m1 = r_0;
      r_0 = r_p1;
      r_p1 += GRID;
   }

   // top of mid band
   // boundary cells

   MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
   MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;
  
   // rest of row
   for ( Uns c = 1; c < c_last; ++c )
   {
      const Uns c_1 = c-1;
      MidOut[r_0+c] = F( &TopIn[c_1], &MidIn[r_0+c_1], &MidIn[r_m1+c_1] );
   }

   // top strip
   r_m1 = GRID*(NROWS-1);
   if ( NxtBtmIn == NULL )
   {
      // boundary strip

      for ( Uns c = c_first; c <= c_last; ++c )
      {
         TopOut[c] = MidIn[r_m1+c] * 0.5f;
      }
   }
   else
   {
      // boundary cells
      TopOut[c_first] = TopIn[c_first+1] * 0.5f;
      TopOut[c_last] = TopIn[c_last-1] * 0.5f;
 
      // rest of row
      for ( Uns c = 1; c < c_last; ++c )
      {
         const Uns c_1 = c-1;
         TopOut[c] = F( &NxtBtmIn[c_1], &TopIn[c_1], &MidIn[r_m1 + c_1] );
      }
   }
}

...

Uns MyCircuit_CalcMthdElem::Process()
{
   // TODO: Add custom process code

   // make references to input stores
   Float* btmNxtIn = (ElemNum() < NB-1) ? this->Trigger1_StripTopTst2Rec( 1 ).Data().Values : NULL;     // NULL if last band
   Float* topIn    = this->Trigger1_StripTopTst2Rec( 0 ).Data().Values();
   Float* midIn    =
this->Trigger1_MidBandTst2Rec( 0 ).Data().Values();
   Float* btmIn    = this->Trigger1_StripBottomTst2Rec( 0 ).Data().Values();
   Float* topPrvIn = (ElemNum() > 0) ? this->Trigger1_StripBottomTst2Rec( 1 ).Data().Values() : NULL;  // NULL if first band

   Uns frameNum = 1 + this->Trigger1_MidBandTst2Rec( 0 ).Data().FrameCount();

   // construct output data
   this->Trigger2_StripTopTst3Rec().Construct( 1 );
   this->Trigger2_StripTopTst3Rec().Initialise( frameNum );

   this->Trigger2_MidBandTst3Rec().Construct( NUM_ROWS );
   this->Trigger2_MidBandTst3Rec().Initialise( frameNum );

   this->Trigger2_StripBottomTst3Rec().Construct( 1 );  
   this->Trigger2_StripBottomTst3Rec().Initialise( frameNum );

   // calculate Wave

   Wave( this->Trigger2_StripTopTst3Rec().Data().Values(),
         this->Trigger2_MidBandTst3Rec().Data().Values(),
         this->Trigger2_StripBottomTst3Rec().Data().Values(),
        
 btmNxtIn,
        
topIn,
         midIn,
         btmIn,
         topPrvIn,
         GRID_SIZE,
         NUM_ROWS );

   // random inject of droplet
   unsigned int row = NUM_ROWS * rand() / RAND_MAX;
   unsigned int col = GRID_SIZE * rand() / RAND_MAX;

   this->Trigger2_MidBandTst3Rec().Data().Values()[row*GRID_SIZE+col] = 1.0f * rand() / RAND_MAX;

   return TRUE;
}

Entering Callback Function Code